 aR  w Q m^9      h	 oP       nSystem-wide
        NAME  MsCalls

;  This Module provides procedure calls interfaces
;  for MsDos Calls.

; The control systemType will determine how much of
; this gets assembled.  If systemType = 2, then all
; of it will be assembled.  If systemType = 1, then
; only the phonelink part will be assembled.  If
; systemType = 0, then only the GRiDLink part will
; be assembled.

DGROUP  GROUP DATA
CGROUP  GROUP CODE

; These are used for GRiDLink, PhoneLink & General

PUBLIC  MsDos

PUBLIC  patched
PUBLIC  msDosIntOffset, msDosIntSegment

PUBLIC  MsGetMachineType
PUBLIC  MsGetDiskXferAddr
PUBLIC  MsGetCurrentDisk, MsSetCurrentDisk
PUBLIC  MsDeviceListPtr
PUBLIC  MsDuplicateFileConn, MsDetach

; These are used for PhoneLink & General

%IF (%systemType EQ 1 OR %systemType EQ 2) THEN (
PUBLIC  MsKeyPressed, MsCharIn, MsCallDriver
) FI

; These are for General

%IF (%systemType EQ 2) THEN (
PUBLIC  MsInMsDosPtr, MsAllocate, MsFree, MsAvailableMemory
PUBLIC  MsCharOut, MsLineOut
PUBLIC  MsLineIn, MsPrintChar, MsPrintLine
PUBLIC  MsAttach, MsRead, MsWrite
PUBLIC  MsSeek, MsRename, MsDelete
PUBLIC  MsAddDirectory, MsDeleteDirectory
PUBLIC  MsSetCurrentDirectory, MsGetCurrentDirectory
PUBLIC  MsFindFirstDirEntry, MsFindNextDirEntry
PUBLIC  MsChangeAttributes, MsIOControl
PUBLIC  MsFCBAttach, MsFCBDetach, MsFCBRead, MsFCBWrite, MsFCBRename
PUBLIC  MsFCBDelete, MsFCBFirstEntry, MsFCBNextEntry
PUBLIC  MsFCBFileSize, MsFCBSetRelativeRecord, MsFCBParseFileName
PUBLIC  MsFlushAllDisks, MsSetDiskXferAddr, MsGetDiskFreeSpace
PUBLIC  MsGetEntrypoint, MsSetCntlCInterrupt
PUBLIC  MsCreateProcess, MsExit, MsGetExitCode
PUBLIC  MsTurnCursorOn, MsTurnCursorOff
PUBLIC  MsGetPDB, MsSetPDB, MsDuplicatePDB
) FI
$EJECT

;                    EQUATES

; file system

oldFile    EQU 0                 ; really 1
updateFile EQU 1                 ; really 2
newFile    EQU 2                 ; really 3

; general

FALSE             EQU 0
TRUE              EQU 1
fnCallInt         EQU 21H

; machine types

cpMachine         EQU 000H
pcMachine         EQU 001H
noMachine         EQU 0FFH

; error codes

eOK               EQU 0
eMsInvalidParm    EQU 1
eMsFileNotFound   EQU 2
eMsPathNotFound   EQU 3
eMsTooManyOpen    EQU 4
eMsAccessDenied   EQU 5
eMsInvalidConn    EQU 6
eMsMemoryTrashed  EQU 7
eMsOutOfMemory    EQU 8
eMsInvalidBlock   EQU 9
eMsBadEnvironment EQU 10
eMsBadFormat      EQU 11
eMsInvalidAccess  EQU 12
eMsInvalidData    EQU 13
eMsInvalidDrive   EQU 15
eMsDirectoryInUse EQU 16
eMsNotSameDevice  EQU 17
eMsNoMoreFiles    EQU 18


; These two variables are used only with interceptor
; If left in their initial state; then the following
; routines will work without changes.

DATA SEGMENT PUBLIC 'DATA'

machineType       DB noMachine

%IF (%systemType EQ 2) THEN (
cursorOn          DB TRUE
cursorType        DW 0607H

cntlCHandlerAddr  DW ?
                  DW 50 DUP (?)
cntlCStackTop     DW ?
) FI

DATA    ENDS
$EJECT

CODE SEGMENT PUBLIC 'CODE'
  ASSUME CS:CGROUP, DS:DGROUP


patched           DB FALSE
msDosIntOffset    DW ?
msDosIntSegment   DW ?


;  MsDos: PROCEDURE CLEAN;

MsDos PROC	NEAR
    TEST CS:patched, TRUE
    JNZ  MsDosIndirect

MsDosInterrupt:
    INT  fnCallInt
    JMP  SHORT MsDosExit

MsDosIndirect:
    PUSHF
    CALL DWORD PTR CS:msDosIntOffset

MsDosExit:
    RET
MsDos ENDP
$EJECT

;  MsGetDiskXferAddr: PROCEDURE PTR CLEAN;

MsGetDiskXferAddr PROC	NEAR

    MOV  AH, 2FH
    CALL MsDos
    RET                 ; ES:BX => Disk Xfer Addr

MsGetDiskXferAddr ENDP


;  MsGetCurrentDisk: PROCEDURE BYTE CLEAN;

MsGetCurrentDisk PROC	NEAR

    MOV  AH, 19H
    CALL MsDos
    RET                 ; AL = Current disk

MsGetCurrentDisk ENDP


;  MsSetCurrentDisk: PROCEDURE (drive) BYTE CLEAN;
;    DCL drive      BYTE;

drive   EQU BYTE  PTR [BP+6]     ; parm 2

MsSetCurrentDisk PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  DL, drive
    MOV  AH, 0EH
    CALL MsDos


    POP  BP             ; AL = # logical drives
    POP  DS
    RET  2

MsSetCurrentDisk ENDP

PURGE drive
$EJECT

FcbType STRUC
  drive     DB 0
  fName     DB 'NUL     '
  ext       DB '   '
  whocares  DB 13 DUP (0)
  pDevEntry DD 0
  extra     DB 8 DUP (0)
FcbType ENDS

fcb  FcbType <>

;  MsDeviceListPtr: PROCEDURE PTR CLEAN;
;
; This routine returns a pointer to the device chain
;

MsDeviceListPtr PROC	NEAR
    PUSH DS
    PUSH BP

    MOV  AH, 30H            ; Get DOS version
    CALL MsDos
    CMP  AL, 3              ; If earlier than version 3.0
    JB   UseFcbMethod       ; then use the Fcb Open (NUL) method
    CMP  AH, 20             ; If earlier than version 3.20
    JB   UseFcbMethod       ; then use the Fcb Open (NUL) method

    MOV  AH, 52H            ; Look at MsDos internal variables
    CALL MsDos
    LES  BX, DWORD PTR ES:[BX+22H]
    JMP  SHORT MsDeviceListReturn

UseFcbMethod:
    XCHG CX, AX             ; Save version in CX

    MOV  AH, 0FH            ; FCB Open call
    PUSH CS
    POP  DS
    MOV  DX, OFFSET fcb
    CALL MsDos              ; FCB Open to MsDos

    MOV  BX, 0FFFFH
    MOV  ES, BX
    MOV  BX, 0FH            ; Return (NULLPTR) for error case
    CMP  AL, 0FFH
    JE   MsDeviceListReturn

    CMP  CL, 3
    JB   OlderThanV30

    LES  BX, fcb.pDevEntry+1
    JMP  SHORT CloseNulOpen

OlderThanV30:
    LES  BX, fcb.pDevEntry  ; pointer to nul device

CloseNulOpen:
    MOV  AH, 10H
    CALL MsDos              ; close the file. (Close doesn't change ES:BX)

MsDeviceListReturn:
    POP  BP
    POP  DS
    RET
MsDeviceListPtr ENDP

PURGE fcb
PURGE FcbType
PURGE drive
PURGE fName
PURGE ext
PURGE whocares
PURGE pDevEntry
PURGE extra
$EJECT

;  MsDuplicateFileConn: PROCEDURE (conn, newConn, pError) WORD CLEAN;
;    DCL conn       WORD;
;    DCL newConn    WORD;
;    DCL pError     PTR;

pError    EQU DWORD PTR [BP+6]      ; parm 3
newConn   EQU WORD  PTR [BP+10]     ; parm 2
conn      EQU WORD  PTR [BP+12]     ; parm 1

MsDuplicateFileConn PROC	NEAR
    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  BX, conn
    MOV  CX, newConn
    CMP  CX, 0FFFFH
    JNE  MsDuplicateForce

MsDuplicateNormal:
    MOV  AH, 45H
    CALL MsDos
    JMP  SHORT MsDuplicateExit

MsDuplicateForce:
    MOV  AH, 46H
    CALL MsDos

MsDuplicateExit:
    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsDuplicateFileConnExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsDuplicateFileConnExit:         ; AX = new conn
    POP  BP
    POP  DS
    RET  8

MsDuplicateFileConn ENDP

PURGE pError, newConn, conn
$EJECT

;  MsDetach: PROCEDURE (conn, pError) CLEAN;
;    DCL conn       WORD;
;    DCL pError     PTR;

pError EQU DWORD PTR [BP+6]      ; parm 2
conn   EQU WORD  PTR [BP+10]     ; parm 1

MsDetach PROC	NEAR
    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  BX, conn
    MOV  AH, 3EH
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsDetachExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsDetachExit:
    POP  BP
    POP  DS
    RET  6

MsDetach ENDP

PURGE pError, conn
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsCreateProcess: PROCEDURE (pPathname, pParmBlock, pError) CLEAN;
;    DCL pPathname   PTR
;    DCL pParmBlock  PTR
;    DCL pError      PTR

pError       EQU DWORD PTR [BP+6]      ; parm 3
pParmBlock   EQU DWORD PTR [BP+10]     ; parm 2
pPathname    EQU DWORD PTR [BP+14]     ; parm 1

usualSS      DW  ?
usualSP      DW  ?

MsCreateProcess PROC	NEAR


    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pPathname
    LES  BX, pParmBlock

;   save the current stack pointer in oldSP and change the stack 
;   so that SS matches with the PDB (CS-10) of executing program
;   This is done to circumvent a bug in IBM PC's loader.
;
    MOV  CS:usualSS, SS
    MOV  CS:usualSP, SP

    MOV  AX, CS
    SUB  AX, 10h
    MOV  SS, AX
    MOV  SP, 100H

    MOV  AX, 4B00H               ; mode = load & go

; zzz note for you Tim from Raj & Dan:  special for gridmgr.
; cannot use call because stack is screwed upon return from int 21
; if function is load & go.  so need to restore stack immediately upon
; return.

    INT  fnCallInt
;    CALL MsDos

; restore the stack 

    MOV  SS, CS:usualSS
    MOV  SP, CS:usualSP

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsCreateProcessExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsCreateProcessExit:
    POP  BP
    POP  DS
    RET  12

MsCreateProcess ENDP

PURGE pError, pParmBlock, pPathname
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsExit: PROCEDURE (exitCode) CLEAN;
;    DCL exitCode  BYTE

exitCode EQU BYTE PTR [BP+4]     ; parm 1

MsExit PROC	NEAR

    PUSH BP
    MOV  BP, SP

    MOV  AL, exitCode
    MOV  AH, 4CH
    CALL MsDos

    POP  BP
    RET  2

MsExit  ENDP

PURGE exitCode


;  MsGetExitCode: PROCEDURE WORD CLEAN;

MsGetExitCode PROC	NEAR

    MOV  AH, 4DH
    CALL MsDos
    RET

MsGetExitCode  ENDP
) FI
$EJECT

%IF (%systemType EQ 1 OR %systemType EQ 2) THEN (

;  MsKeyPressed: PROCEDURE BOOLEAN CLEAN;

MsKeyPressed PROC	NEAR

    MOV  AH, 0BH
    CALL MsDos

    RET

MsKeyPressed ENDP
) FI


%IF (%systemType EQ 1 OR %systemType EQ 2) THEN (

;  MsCharIn: PROCEDURE BYTE CLEAN;

MsCharIn PROC	NEAR

    MOV  AH, 07H ; Cntl-C Interrupt Check Disabled
    CALL MsDos

    RET

MsCharIn ENDP
) FI
$EJECT

%IF (%systemType EQ 1 OR %systemType EQ 2) THEN (

;  MsCallDriver: PROCEDURE (pDeviceHeader, pRequestBlock) CLEAN;
;    DCL pDeviceHeader   PTR;
;    DCL deviceHeader    BASED pDeviceHeader MsDeviceHeaderType;
;
;    DCL pRequestBlock   PTR;
;    DCL requestBlock    BASED pRequestBlock MsGenericRequestBlockType;

pRequestBlock    EQU DWORD PTR [BP+6]   ; parm 2
pDeviceHeader    EQU DWORD PTR [BP+10]  ; parm 1

pEntrypoint      EQU DWORD PTR [BP-4]
pEntrySeg        EQU WORD  PTR [BP-2]
pEntryOff        EQU WORD  PTR [BP-4]

deviceHeader	    STRUC

nextDevice          DD ?
attributes          DW ?
strategyEntryPoint  DW ?
interruptEntryPoint DW ?

deviceHeader     ENDS

MsCallDriver PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    PUSH AX                      ; make space for pTemp     
    PUSH AX                      ; make space for pTemp

    LES  BX, pRequestBlock       ; ES:BX => request block
    LDS  SI, pDeviceHeader       ; DS:SI => points to deviceHeader

    MOV  AX, DS:[SI].strategyEntryPoint
    MOV  pEntryOff, AX
    MOV  pEntrySeg, DS
    CALL pEntrypoint

; Need to check error and return

    MOV  AX, DS:[SI].interruptEntryPoint
    MOV  pEntryOff, AX
    CALL pEntrypoint

    MOV  SP, BP
    POP  BP
    POP  DS
    RET  8

MsCallDriver ENDP

PURGE pEntrypoint, pEntryOff, pEntrySeg
PURGE pDeviceHeader, pRequestBlock, deviceHeader
PURGE nextDevice, attributes, strategyEntryPoint, interruptEntryPoint
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsCharOut: PROCEDURE (char) CLEAN;
;    DCL char       BYTE;

char EQU BYTE PTR [BP+4]         ; parm 1

MsCharOut PROC	NEAR

    PUSH BP
    MOV  BP, SP

    MOV  DL, char
    CMP  DL, 0FFH
    JE   MsCharOutExit

    MOV  AH, 06H ; Cntl-C Interrupt Check Disabled
    CALL MsDos

MsCharOutExit:
    POP  BP
    RET  2

MsCharOut ENDP

PURGE char


;  MsLineOut: PROCEDURE (pLineOut) CLEAN;
;    DCL pLine      PTR;

pLineOut EQU DWORD PTR [BP+4]    ; parm 1

MsLineOut PROC	NEAR

    PUSH BP
    MOV  BP, SP
    LES  BX, pLineOut

MsLineOutLoop:
    MOV  AL, ES:[BX]
    CMP  AL, '$'
    JE   MsLineOutExit

    PUSH AX
    CALL MsCharOut ;Cntl-C Interrupt Check Disabled

    INC  BX
    JMP  SHORT MsLineOutLoop

MsLineOutExit:
    POP  BP
    RET  4

MsLineOut ENDP

PURGE pLineOut
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsLineIn: PROCEDURE (pLineIn) CLEAN;
;    DCL pLineIn    PTR;

pLineIn EQU DWORD PTR [BP+6]     ; parm 1

MsLineIn PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pLineIn
    MOV  AH, 0AH
    CALL MsDos

    POP  BP
    POP  DS
    RET  4

MsLineIn ENDP

PURGE pLineIn
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsPrintChar: PROCEDURE (char) CLEAN;
;    DCL char       BYTE;

char EQU BYTE PTR [BP+4]         ; parm 1

MsPrintChar PROC	NEAR

    PUSH BP
    MOV  BP, SP

    MOV  DL, char
    MOV  AH, 05H
    CALL MsDos

    POP  BP
    RET  2

MsPrintChar ENDP

PURGE char
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsPrintLine: PROCEDURE (pLineOut) CLEAN;
;    DCL pLine      PTR;

pLineOut EQU DWORD PTR [BP+4]    ; parm 1

MsPrintLine PROC	NEAR

    PUSH BP
    MOV  BP, SP

    LES  BX, pLineOut

MsPrintLineLoop:
    MOV  DL, ES:[BX]
    CMP  DL, '$'
    JE   MsPrintLineExit

    MOV  AH, 05H
    CALL MsDos

    INC  BX
    JMP  SHORT MsPrintLineLoop

MsPrintLineExit:
    POP  BP
    RET  4

MsPrintLine ENDP

PURGE pLineOut
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsAttach: PROCEDURE (pPathName, mode, access, attribute, pError) WORD CLEAN;
;    DCL mode       BYTE;
;    DCL access     BYTE;
;    DCL attribute  WORD;
;    DCL pPathname  PTR;
;    DCL pError     PTR;

pError     EQU DWORD PTR [BP+6]  ; parm 5
attribute  EQU WORD  PTR [BP+10] ; parm 4
access     EQU BYTE  PTR [BP+12] ; parm 3
mode       EQU BYTE  PTR [BP+14] ; parm 2
pPathname  EQU DWORD PTR [BP+16] ; parm 1

MsAttach PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  AL, mode
    DEC  AL
    CMP  AL, newFile
    JG   MsAttachInvalidMode

    LDS  DX, pPathname           ; DS:DX => pathname

    CMP  AL, oldFile
    JE   MsAttachIsOpen

MsAttachIsCreate:
    MOV  CX, attribute
    MOV  AH, 3CH
    CALL MsDos                   ; create
    JMP  SHORT MsAttachCheckError

MsAttachIsOpen:
    MOV  AL, access
    MOV  AH, 3DH
    CALL MsDos                   ; open
    JMP  SHORT MsAttachCheckError

MsAttachInvalidMode:
    MOV  AX, eMsInvalidParm
    STC

MsAttachCheckError:
    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsAttachExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsAttachExit:
    POP  BP                      ; AX = conn
    POP  DS
    RET  14

MsAttach ENDP

PURGE pError, attribute, access, mode, pPathname
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsRead: PROCEDURE (conn, pBuffer, count, pError) WORD CLEAN;
;    DCL conn       WORD;
;    DCL count      WORD;
;    DCL pBuffer    PTR;
;    DCL pError     PTR;

pError    EQU DWORD PTR [BP+6]   ; parm 4
count     EQU WORD  PTR [BP+10]  ; parm 3
pBuffer   EQU DWORD PTR [BP+12]  ; parm 2
conn      EQU WORD  PTR [BP+16]  ; parm 1

MsRead PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  BX, conn
    MOV  CX, count
    LDS  DX, pBuffer
    MOV  AH, 3FH
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsReadExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsReadExit:
    POP  BP                      ; AX = # bytes read
    POP  DS
    RET  12

MsRead ENDP

PURGE pError, count, pBuffer, conn
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsWrite: PROCEDURE (conn, pBuffer, count, pError) WORD CLEAN;
;    DCL conn       WORD;
;    DCL count      WORD;
;    DCL pBuffer    PTR;
;    DCL pError     PTR;

pError    EQU DWORD PTR [BP+6]   ; parm 4
count     EQU WORD  PTR [BP+10]  ; parm 3
pBuffer   EQU DWORD PTR [BP+12]  ; parm 2
conn      EQU WORD  PTR [BP+16]  ; parm 1

MsWrite PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  BX, conn
    MOV  CX, count
    LDS  DX, pBuffer
    MOV  AH, 40H
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsWriteExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsWriteExit:
    POP  BP                      ; AX = # bytes written
    POP  DS
    RET  12

MsWrite ENDP

PURGE pError, count, pBuffer, conn
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsSeek: PROCEDURE (conn, mode, count, pError) DWORD CLEAN;
;    DCL mode       BYTE;
;    DCL conn       WORD;
;    DCL count      DWORD;
;    DCL pError     PTR;

seekBegPlus EQU 0
seekCurPlus EQU 1
seekEndPlus EQU 2

pError      EQU DWORD PTR [BP+6]  ; parm 4
count       EQU DWORD PTR [BP+10] ; parm 3
mode        EQU BYTE  PTR [BP+14] ; parm 2
conn        EQU WORD  PTR [BP+16] ; parm 1

MsSeek PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, count
    MOV  CX, DS                  ; CX:DX => count
    MOV  BX, conn                ; BX = conn
    MOV  AL, mode                ; AL = mode
    MOV  AH, 42H
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsSeekExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsSeekExit:
    POP  BP                      ; DX:AX => new file position
    POP  DS
    RET  12

MsSeek ENDP

PURGE pError, count, mode, conn
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsRename: PROCEDURE (pOldName, pNewName, pError) CLEAN;
;    DCL pOldName   PTR;
;    DCL pNewName   PTR;
;    DCL pError     PTR;

pError    EQU DWORD PTR [BP+6]   ; parm 3
pNewName  EQU DWORD PTR [BP+10]  ; parm 2
pOldName  EQU DWORD PTR [BP+14]  ; parm 1

MsRename PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pOldName
    LES  DI, pNewName
    MOV  AH, 56H
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsRenameExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsRenameExit:
    POP  BP
    POP  DS
    RET  12

MsRename ENDP

PURGE pError, pNewName, pOldName
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsDelete: PROCEDURE (pPathname, pError) CLEAN;
;    DCL pPathname  PTR;
;    DCL pError     PTR;

pError    EQU DWORD PTR [BP+6]   ; parm 2
pPathName EQU DWORD PTR [BP+10]  ; parm 1

MsDelete PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pPathname
    MOV  AH, 41H
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsDeleteExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsDeleteExit:
    POP  BP
    POP  DS
    RET  8

MsDelete ENDP

PURGE pError, pPathname
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsAddDirectory: PROCEDURE (pDirName, pError) CLEAN;
;    DCL pDirName   PTR;
;    DCL pError     PTR;

pError    EQU DWORD PTR [BP+6]      ; parm 2
pDirName  EQU DWORD PTR [BP+10]     ; parm 1

MsAddDirectory PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pDirName
    MOV  AH, 39H
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsAddDirectoryExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsAddDirectoryExit:
    POP  BP
    POP  DS
    RET  8

MsAddDirectory ENDP

PURGE pError, pDirName
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsDeleteDirectory: PROCEDURE (pDirName, pError) CLEAN;
;    DCL pDirName   PTR;
;    DCL pError     PTR;

pError    EQU DWORD PTR [BP+6]      ; parm 2
pDirName  EQU DWORD PTR [BP+10]     ; parm 1

MsDeleteDirectory PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pDirName
    MOV  AH, 3AH
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsDeleteDirectoryExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsDeleteDirectoryExit:
    POP  BP
    POP  DS
    RET  8

MsDeleteDirectory ENDP

PURGE pError, pDirName
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsSetCurrentDirectory: PROCEDURE (pDirName, pError) CLEAN;
;    DCL pDirName   PTR;
;    DCL pError     PTR;

pError    EQU DWORD PTR [BP+6]      ; parm 2
pDirName  EQU DWORD PTR [BP+10]     ; parm 1

MsSetCurrentDirectory PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pDirName
    MOV  AH, 3BH
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsSetCurrentDirectoryExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsSetCurrentDirectoryExit:
    POP  BP
    POP  DS
    RET  8

MsSetCurrentDirectory ENDP

PURGE pError, pDirName
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsGetCurrentDirectory: PROCEDURE (drive, pDirName, pError) CLEAN;
;    DCL drive      BYTE;
;    DCL pDirName   PTR;
;    DCL pError     PTR;

pError    EQU DWORD PTR [BP+6]      ; parm 3
pDirName  EQU DWORD PTR [BP+10]     ; parm 2
drive     EQU BYTE  PTR [BP+14]     ; parm 1

MsGetCurrentDirectory PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  DL, drive
    LDS  DX, pDirName
    MOV  AH, 47H
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsGetCurrentDirectoryExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsGetCurrentDirectoryExit:
    POP  BP
    POP  DS
    RET  10

MsGetCurrentDirectory ENDP

PURGE pError, pDirName, drive
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFindFirstDirEntry: PROCEDURE (pPathname, attr, pError) CLEAN;
;    DCL attr       WORD;
;    DCL pPathName  PTR;
;    DCL pError     PTR;

pError    EQU DWORD PTR [BP+6]      ; parm 3
attr      EQU WORD  PTR [BP+10]     ; parm 2
pPathName EQU DWORD PTR [BP+12]     ; parm 1

MsFindFirstDirEntry PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  CX, attr
    LDS  DX, pPathName
    MOV  AH, 4EH
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsFindFirstDirEntryExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsFindFirstDirEntryExit:
    POP  BP
    POP  DS
    RET  10

MsFindFirstDirEntry ENDP

PURGE pError, attr, pPathname
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFindNextDirEntry: PROCEDURE (pError) CLEAN;
;    DCL pError     PTR;

pError    EQU DWORD PTR [BP+6]      ; parm 1

MsFindNextDirEntry PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  AH, 4FH
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsFindNextDirEntryExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsFindNextDirEntryExit:
    POP  BP
    POP  DS
    RET  4

MsFindNextDirEntry ENDP

PURGE pError
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsChangeAttributes: PROCEDURE (pPathname, attr, mode, pError) WORD CLEAN;
;    DCL mode       BYTE;
;    DCL attr       WORD;
;    DCL pPathname  PTR;
;    DCL pError     PTR;

pError    EQU DWORD PTR [BP+6]      ; parm 4
mode      EQU BYTE  PTR [BP+10]     ; parm 3
attr      EQU WORD  PTR [BP+12]     ; parm 2
pPathname EQU DWORD PTR [BP+14]     ; parm 1

MsChangeAttributes PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  AL, mode
    MOV  CX, attr
    LDS  DX, pPathname

    MOV  AH, 43H
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsChangeAttributesExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsChangeAttributesExit:
    MOV  AX, CX                  ; attributes
    POP  BP
    POP  DS
    RET  12

MsChangeAttributes ENDP

PURGE pError, mode, attr, pPathname
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsIOControl: PROCEDURE (conn, pBuffer, count, mode, pError) WORD CLEAN;
;    DCL mode       BYTE;
;    DCL conn       WORD;
;    DCL count      WORD;
;    DCL pBuffer    PTR;
;    DCL pError     PTR;

pError    EQU DWORD PTR [BP+6]   ; parm 5
mode      EQU BYTE  PTR [BP+10]  ; parm 4
count     EQU WORD  PTR [BP+12]  ; parm 3
pBuffer   EQU DWORD PTR [BP+14]  ; parm 2
conn      EQU WORD  PTR [BP+18]  ; parm 1

MsIOControl PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  AL, mode
    MOV  BX, conn
    MOV  CX, count
    LDS  DX, pBuffer

    MOV  AH, 44H
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsIOControlExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsIOControlExit:
    POP  BP                      ; AX = Return info
    POP  DS
    RET  14

MsIOControl ENDP

PURGE pError, mode, count, pBuffer, conn
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFCBAttach: PROCEDURE (pFCB, mode, pStatus) WORD CLEAN;
;    DCL mode       BYTE;
;    DCL pFCB       PTR;
;    DCL pStatus    PTR;

pStatus   EQU DWORD PTR [BP+6]   ; parm 3
mode      EQU BYTE  PTR [BP+10]  ; parm 2
pFCB      EQU DWORD PTR [BP+12]  ; parm 1

MsFCBAttach PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pFCB

    MOV  AL, mode
    CMP  AL, newFile
    JE   MsFCBAttachCreate

MsFCBAttachOpen:
    MOV  AH, 0FH
    CALL MsDos
    JMP  SHORT MsFCBAttachExit

MsFCBAttachCreate:
    MOV  AH, 16H
    CALL MsDos

MsFCBAttachExit:
    LDS  DI, pStatus
    MOV  DS:[DI], AL

    POP  BP
    POP  DS
    RET  10

MsFCBAttach ENDP

PURGE pStatus, mode, pFCB
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFCBDetach: PROCEDURE (pFCB, pStatus) CLEAN;
;    DCL pFCB       PTR;
;    DCL pStatus    PTR;

pStatus   EQU DWORD PTR [BP+6]   ; parm 2
pFCB      EQU DWORD PTR [BP+10]  ; parm 1

MsFCBDetach PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pFCB

    MOV  AH, 10H
    CALL MsDos

    LDS  DI, pStatus
    MOV  DS:[DI], AL

    POP  BP
    POP  DS
    RET  8

MsFCBDetach ENDP

PURGE pStatus, pFCB
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFCBRead: PROCEDURE (pFCB, mode, count, pStatus) WORD CLEAN;
;    DCL mode       BYTE;
;    DCL count      WORD;
;    DCL pFCB       PTR;
;    DCL pStatus    PTR;

pStatus   EQU DWORD PTR [BP+6]   ; parm 4
count     EQU WORD  PTR [BP+10]  ; parm 3
mode      EQU BYTE  PTR [BP+12]  ; parm 2
pFCB      EQU DWORD PTR [BP+14]  ; parm 1

MsFCBRead PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  CX, count
    LDS  DX, pFCB

    MOV  AL, mode
    OR   AL, AL
    JNZ  MsFCBRndRead

MsFCBSeqRead:
    MOV  AH, 14H              ; mode = 0 = seq
    CALL MsDos
    JMP  SHORT MsFCBReadExit

MsFCBRndRead:
    DEC  AL
    JNZ  MsFCBRndBlkRead

    MOV  AH, 21H              ; mode = 1 = rnd
    CALL MsDos
    JMP  SHORT MsFCBReadExit

MsFCBRndBlkRead:
    DEC  AL
    MOV  AL, 0FFH
    JNZ  MsFCBReadExit

    MOV  AH, 27H              ; mode = 2 = rnd blk
    CALL MsDos

MsFCBReadExit:
    LDS  DI, pStatus
    MOV  DS:[DI], AL

    POP  BP
    POP  DS
    RET  12

MsFCBRead ENDP

PURGE pStatus, count, mode, pFCB
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFCBWrite: PROCEDURE (pFCB, mode, count, pStatus) WORD CLEAN;
;    DCL mode       BYTE;
;    DCL count      WORD;
;    DCL pFCB       PTR;
;    DCL pStatus    PTR;

pStatus   EQU DWORD PTR [BP+6]   ; parm 4
count     EQU WORD  PTR [BP+10]  ; parm 3
mode      EQU BYTE  PTR [BP+12]  ; parm 2
pFCB      EQU DWORD PTR [BP+14]  ; parm 1

MsFCBWrite PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  CX, count
    LDS  DX, pFCB

    MOV  AL, mode
    OR   AL, AL
    JNZ  MsFCBRndWrite

MsFCBSeqWrite:
    MOV  AH, 15H              ; mode = 0 = seq
    CALL MsDos
    JMP  SHORT MsFCBWriteExit

MsFCBRndWrite:
    DEC  AL
    JNZ  MsFCBRndBlkWrite

    MOV  AH, 22H              ; mode = 1 = rnd
    CALL MsDos
    JMP  SHORT MsFCBWriteExit

MsFCBRndBlkWrite:
    DEC  AL
    MOV  AL, 0FFH
    JNZ  MsFCBWriteExit

    MOV  AH, 28H              ; mode = 2 = rnd blk
    CALL MsDos

MsFCBWriteExit:
    LDS  DI, pStatus
    MOV  DS:[DI], AL

    MOV  AX, CX

    POP  BP
    POP  DS
    RET  12

MsFCBWrite ENDP

PURGE pStatus, count, mode, pFCB
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFCBRename: PROCEDURE (pFCB, pStatus) CLEAN;
;    DCL pFCB       PTR;
;    DCL pStatus    PTR;

pStatus   EQU DWORD PTR [BP+6]   ; parm 2
pFCB      EQU DWORD PTR [BP+10]  ; parm 1

MsFCBRename PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pFCB

    MOV  AH, 17H
    CALL MsDos

    LDS  DI, pStatus
    MOV  DS:[DI], AL

    POP  BP
    POP  DS
    RET  8

MsFCBRename ENDP

PURGE pStatus, pFCB
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFCBDelete: PROCEDURE (pFCB, pStatus) CLEAN;
;    DCL pFCB       PTR;
;    DCL pStatus    PTR;

pStatus   EQU DWORD PTR [BP+6]   ; parm 2
pFCB      EQU DWORD PTR [BP+10]  ; parm 1

MsFCBDelete PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pFCB
    MOV  AH, 13H
    CALL MsDos

    LDS  DI, pStatus
    MOV  DS:[DI], AL

    POP  BP
    POP  DS
    RET  8

MsFCBDelete ENDP

PURGE pStatus, pFCB
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFCBFirstEntry: PROCEDURE (pFCB, pStatus) CLEAN;
;    DCL pFCB       PTR;
;    DCL pStatus    PTR;

pStatus   EQU DWORD PTR [BP+6]   ; parm 2
pFCB      EQU DWORD PTR [BP+10]  ; parm 1

MsFCBFirstEntry PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pFCB
    MOV  AH, 11H
    CALL MsDos

    LDS  DI, pStatus
    MOV  DS:[DI], AL

    POP  BP
    POP  DS
    RET  8

MsFCBFirstEntry ENDP

PURGE pStatus, pFCB
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFCBNextEntry: PROCEDURE (pFCB, pStatus) CLEAN;
;    DCL pFCB       PTR;
;    DCL pStatus    PTR;

pStatus   EQU DWORD PTR [BP+6]   ; parm 2
pFCB      EQU DWORD PTR [BP+10]  ; parm 1

MsFCBNextEntry PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pFCB
    MOV  AH, 12H
    CALL MsDos

    LDS  DI, pStatus
    MOV  DS:[DI], AL

    POP  BP
    POP  DS
    RET  8

MsFCBNextEntry ENDP

PURGE pStatus, pFCB
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFCBFileSize: PROCEDURE (pFCB, pStatus) CLEAN;
;    DCL pFCB       PTR;
;    DCL pStatus    PTR;

pStatus   EQU DWORD PTR [BP+6]   ; parm 2
pFCB      EQU DWORD PTR [BP+10]  ; parm 1

MsFCBFileSize PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pFCB
    MOV  AH, 23H
    CALL MsDos

    LDS  DI, pStatus
    MOV  DS:[DI], AL

    POP  BP
    POP  DS
    RET  8

MsFCBFileSize ENDP

PURGE pStatus, pFCB
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFCBSetRelativeRecord: PROCEDURE (pFCB) CLEAN;
;    DCL pFCB       PTR;

pFCB      EQU DWORD PTR [BP+6]   ; parm 1

MsFCBSetRelativeRecord PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP


    LDS  DX, pFCB
    MOV  AH, 24H
    CALL MsDos

    POP  BP
    POP  DS
    RET  4

MsFCBSetRelativeRecord ENDP

PURGE pFCB
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFCBParseFileName: PROCEDURE (controls, pString, pFCB, pStatus) PTR CLEAN;
;    DCL controls   BYTE;
;    DCL pString    PTR;
;    DCL pFCB       PTR;
;    DCL pStatus    PTR;

pStatus   EQU DWORD PTR [BP+6]   ; parm 4
pFCB      EQU DWORD PTR [BP+10]  ; parm 3
pString   EQU DWORD PTR [BP+14]  ; parm 2
controls  EQU BYTE  PTR [BP+18]  ; parm 1

MsFCBParseFileName PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LES  DI, pFCB
    LDS  SI, pString
    MOV  AL, controls
    MOV  AH, 29H
    CALL MsDos

    PUSH DS            ; DS:SI => next part of string
    POP  ES
    MOV  BX, SI        ; Return it in ES:BX

    LDS  DI, pStatus
    MOV  DS:[DI], AL   ; AL = error

    POP  BP
    POP  DS
    RET  14

MsFCBParseFileName ENDP

PURGE pStatus, pFCB, pString, controls
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFlushAllDisks: PROCEDURE CLEAN;

MsFlushAllDisks PROC	NEAR

    MOV  AH, 0DH
    CALL MsDos
    RET

MsFlushAllDisks ENDP
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsSetDiskXferAddr: PROCEDURE (pAddr) CLEAN;
;    DCL pAddr      PTR;

pAddr   EQU DWORD PTR [BP+6]     ; parm 1

MsSetDiskXferAddr PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LDS  DX, pAddr
    MOV  AH, 1AH
    CALL MsDos


    POP  BP
    POP  DS
    RET  4

MsSetDiskXferAddr ENDP

PURGE pAddr
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsGetDiskFreeSpace: PROCEDURE (drive, pInfo) CLEAN;
;    DCL drive      BYTE;
;    DCL pInfo      PTR;

pInfo   EQU DWORD PTR [BP+6]     ; parm 2
drive   EQU BYTE  PTR [BP+10]    ; parm 1

MsGetDiskFreeSpace PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  DL, drive
    MOV  AH, 36H
    CALL MsDos

    LDS  DI, pInfo
    MOV  [DI+0], BX
    MOV  [DI+2], DX
    MOV  [DI+4], CX
    MOV  [DI+6], AX

    POP  BP
    POP  DS
    RET  6

MsGetDiskFreeSpace ENDP

PURGE pInfo, drive
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsAllocate: PROCEDURE (numBlks, pError) PTR CLEAN;
;    DCL numBlks    WORD;
;    DCL pError     PTR;

pError EQU DWORD PTR [BP+6]        ; parm 2
numBlks    EQU   WORD  PTR [BP+10] ; parm 1

MsAllocate PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    MOV  BX, numBlks
    MOV  AH, 48H
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsAllocateExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsAllocateExit:
    MOV  ES, AX
    XOR  BX, BX                  ; ES:BX => Memory

    POP  BP
    POP  DS
    RET  6

MsAllocate ENDP

PURGE pError, numBlks
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsFree: PROCEDURE (pBlock, pError) CLEAN;
;    DCL pBlock     PTR;
;    DCL pError     PTR;

pError EQU DWORD PTR [BP+6]      ; parm 2
pBlock EQU DWORD PTR [BP+10]     ; parm 1

MsFree PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LES  SI, pBlock
    MOV  AH, 49H
    CALL MsDos

    LDS  DI, pError
    MOV  WORD PTR DS:[DI], eOK
    JNC  MsFreeExit

    MOV  DS:[DI], AX             ; DS:DI => Error

MsFreeExit:
    POP  BP
    POP  DS
    RET  8

MsFree ENDP

PURGE pError, pBlock
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsAvailableMemory: PROCEDURE WORD CLEAN;

MsAvailableMemory PROC	NEAR

    MOV  BX, 0FFFFH
    MOV  AH, 48H                 ; Allocate
    CALL MsDos
    MOV  AX, BX
    RET

MsAvailableMemory ENDP
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsInMsDosPtr: PROCEDURE PTR CLEAN;


MsInMsDosPtr PROC	NEAR

    MOV  AH, 34H
    CALL MsDos
    RET

MsInMsDosPtr  ENDP
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsGetEntrypoint: PROCEDURE (pSignature, pError) PTR CLEAN;
;    DCL pDeviceHeader   PTR;
;    DCL pRequestBlock   PTR;

pError     EQU DWORD PTR [BP+6]  ; parm 2
pSignature EQU DWORD PTR [BP+10] ; parm 1

MsGetEntrypoint PROC	NEAR

    PUSH DS
    PUSH BP
    MOV  BP, SP

    LES  BX, pSignature
    LDS  DI, pError
    PUSH DS
    PUSH DI                      ; save @error

    MOV  CX, BX                  ; pSignature is
    MOV  BX, ES                  ; passed in BX:CX
    XOR  AX, AX
    MOV  DX, AX                  ; Registers
    MOV  DI, AX                  ; DX, DI, SI, BP
    MOV  SI, AX                  ; must be zero
    MOV  BP, AX
    DEC  AX
    MOV  DS, AX                  ; DS & ES must be
    MOV  ES, AX                  ; NULLWORD

    MOV  AX, 0C98H               ; Use flush kbd
    CALL MsDos                   ; fnc w/ 98H

    INC  AX                      ; Upon return
    JNZ  MsGetEntryErrorExit     ; AX = NULLWORD

    MOV  AX, DS                  ; And DS must
    OR   AX, AX                  ; be zero
    JNZ  MsGetEntryErrorExit

    INC  DX
    INC  DI
    INC  SI
    INC  BP

    MOV  AX, ES                  ; And ES must
    OR   AX, DX                  ; be zero; plus
    OR   AX, DI                  ; registers DX,
    OR   AX, SI                  ; DI, SI and BP
    OR   AX, BP                  ; must be NULLWORD
    JNZ  MsGetEntryErrorExit     ; for valid return

    MOV  ES, BX                  ; @ entrypoint is
    MOV  BX, CX                  ; in BX:CX
    JMP  SHORT MsGetEntrypointExit

MsGetEntryErrorExit:
    MOV  AX, 0FFFFH
    MOV  ES, AX
    MOV  BX, 0FH                 ; RETURN (NULLPTR)

MsGetEntrypointExit:
    POP  DI
    POP  DS                      ; @error
    MOV  DS:[DI], AX

    POP  BP
    POP  DS
    RET  8

MsGetEntrypoint ENDP

PURGE pError, pSignature
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsSetCntlCInterrupt: PROCEDURE (cntlCRtnOffset) CLEAN;
; Cntl-C Interrupt number is 23H

cntlCRtnOffset EQU WORD PTR [BP+4]

MsSetCntlCInterrupt PROC	NEAR
    PUSH BP
    MOV  BP, SP

    MOV  AX, 3301H  ; Set Cntl-C Check State
    MOV  DL, 0      ; To off
    CALL MsDos

    MOV  AX, cntlCRtnOffset
    MOV  cntlCHandlerAddr, AX

    PUSH DS
    PUSH CS
    POP  DS
    MOV  DX, OFFSET CntlCHandler
    MOV  AX, 2523H
    CALL MsDos
    POP  DS

    POP  BP
    RET  2
MsSetCntlCInterrupt ENDP
) FI

$EJECT

%IF (%systemType EQ 2) THEN (

userStackSS DW ?
userStackSP DW ?

CntlCHandler PROC FAR
    MOV  CS:userStackSS, SS
    MOV  CS:userStackSP, SP

    MOV  SP, SEG DGROUP:cntlCStackTop
    MOV  SS, SP
    MOV  SP, OFFSET DGROUP:cntlCStackTop

    PUSH AX
    PUSH BX
    PUSH CX
    PUSH DX
    PUSH SI
    PUSH DI
    PUSH BP
    PUSH DS
    PUSH ES

    PUSH SS
    POP  DS

    MOV  DI, cntlCHandlerAddr
    CALL DI

    POP  ES
    POP  DS
    POP  BP
    POP  DI
    POP  SI
    POP  DX
    POP  CX
    POP  BX
    POP  AX

    MOV  SS, CS:userStackSS
    MOV  SP, CS:userStackSP

    IRET
CntlCHandler ENDP
) FI
$EJECT

;  Video Routines: PROCEDURE CLEAN;

VideoRtns PROC NEAR
    PUSH BP
    INT  10H                     ; PC Video Interrupt
    POP  BP
    RET
VideoRtns ENDP


;  MsGetMachineType: PROCEDURE BYTE CLEAN;

MsGetMachineType PROC	NEAR
    MOV  AL, machineType
    CMP  AL, noMachine
    JNE  MsGetMachineTypeExit

    MOV  AX, 0FFFH               ; Current Video State
    CALL VideoRtns               ; PC Video Interrupt
    INC  AL                      ; If AL still = 0FF
    JZ   MsGetMachineTypeSetIt   ; Type of 0 => Compass
    MOV  AL, pcMachine           ; Type of 1 => PC

MsGetMachineTypeSetIt:
    MOV  machineType, AL

MsGetMachineTypeExit:
    RET

MsGetMachineType ENDP
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsTurnCursorOn: PROCEDURE CLEAN;

MsTurnCursorOn PROC	NEAR
    PUSH BP
    MOV  BP, SP

    TEST cursorOn, TRUE           ; If cursor is already on
    JNZ  MsTurnCursorOnExit       ; then do nothing

    MOV  cursorOn, TRUE           ; Set cursor state to on

    CALL MsGetMachineType
    OR   AL, AL                   ; If type = 0
    JZ   MsTurnCpCursorOn         ; then compass

    DEC  AL                       ; Else if not = 1
    JNZ  MsTurnCursorOnExit       ; then do nothing

MsTurnPcCursorOn:
    MOV  AH, 15                   ; Get curr state
    CALL VideoRtns                ; Video interrupt
    MOV  CX, cursorType           ; Get old cursor
    AND  CH, 0DFH                 ; Turn on cursor
    CMP  CL, 67H                  ; If NOT ROM bug version
    JNE  SetCursorOn              ; then use saved mode

    MOV  CX, 0607H                ; Default graphics display cursor mode
    CMP  AL, 7                    ; If not on monochrome display
    JNE  SetCursorOn              ; then use graphics values

    MOV  CX, 0B0CH                ; else use monochrome default cursor mode

SetCursorOn:
    MOV  AH, 1                    ; Set cursor type
    CALL VideoRtns                ; Video Interrupt
    JMP  SHORT MsTurnCursorOnExit ; Exit

MsTurnCpCursorOn:
    MOV  AX, OFFSET cursorOnStr   ; Send ANSI
    PUSH CS                       ; escape sequence
    PUSH AX                       ; to turn cursor
    CALL MsLineOut                ; on

MsTurnCursorOnExit:
    POP  BP
    RET
MsTurnCursorOn ENDP

cursorOnStr DB 1BH, '[3;3z$'
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsTurnCursorOff: PROCEDURE CLEAN;

MsTurnCursorOff PROC	NEAR
    PUSH BP
    MOV  BP, SP

    TEST cursorOn, TRUE           ; If the cursor is already
    JZ   MsTurnCursorOffExit      ; off then do nothing

    MOV  cursorOn, FALSE          ; Set cursor state to off

    CALL MsGetMachineType
    OR   AL, AL                   ; If type = 0
    JZ   MsTurnCpCursorOff        ; then compass

    DEC  AL                       ; Else if not = 1
    JNZ  MsTurnCursorOffExit      ; then do nothing


MsTurnPcCursorOff:
    MOV  AH, 15                   ; Get curr state
    CALL VideoRtns                ; Video interrupt
    MOV  AH, 3                    ; Get cursor type
    CALL VideoRtns                ; Video interrupt
    MOV  cursorType, CX           ; Save old cursor
    OR   CH, 20H                  ; Turn off cursor
    MOV  AH, 1                    ; Set cursor type
    CALL VideoRtns                ; Video interrupt
    JMP  SHORT MsTurnCursorOffExit

MsTurnCpCursorOff:
    MOV  AX, OFFSET cursorOffStr  ; Send ANSI esc
    PUSH CS                       ; sequence to
    PUSH AX                       ; turn cursor
    CALL MsLineOut                ; off

MsTurnCursorOffExit:
    POP  BP
    RET
MsTurnCursorOff ENDP

cursorOffStr DB 1BH, '[3;4z$'
) FI
$EJECT

%IF (%systemType EQ 2) THEN (

;  MsGetPDB: PROCEDURE SELECTOR CLEAN;

MsGetPDB PROC	NEAR
    MOV  AH, 51H
    CALL MsDos
    MOV  AX, BX
    RET
MsGetPDB ENDP


;  MsSetPDB: PROCEDURE (sPDB) CLEAN;

sPDB EQU WORD PTR [BP+4]

MsSetPDB PROC	NEAR
    PUSH BP
    MOV  BP, SP

    MOV  BX, sPDB
    MOV  AH, 50H
    CALL MsDos

    POP  BP
    RET  2
MsSetPDB ENDP

PURGE sPDB


;  MsDuplicatePDB: PROCEDURE (sPDB, hiMem) CLEAN;

sPDB  EQU WORD PTR [BP+6]
hiMem EQU WORD PTR [BP+4]

MsDuplicatePDB PROC	NEAR
    PUSH BP
    MOV  BP, SP

    MOV  DX, sPDB
    MOV  SI, hiMem
    MOV  AH, 55H
    CALL MsDos

    POP  BP
    RET  4
MsDuplicatePDB ENDP

PURGE sPDB, hiMem
) FI
$EJECT

$LIST
    NOP
$NOLIST

CODE    ENDS

        END
